﻿using System;
using System.Windows.Markup;
using ICodeShare.UI.Converters.Helpers;

namespace ICodeShare.UI.Converters.Markup
{
    #region MarkupExtension
    /// <summary>
    /// 表达式转换器
    /// </summary>
    /// <example>
    /// 1.在xmal文件引用DateConverter类所在命名空间。
    ///    xmlns:cvts="http://schemas.extended.wpf.com/converters"
    /// 2.在xaml文件中指定Binding值的Converter
    ///    <Slider x:Name="_slider"/>
    ///    <Label Content="{Binding Value, ElementName=_slider, Converter={cvts:ExpressionConverter {}{0} * 2}}"/>
    ///    <StackPanel x:Name="_panel">
    ///       <Popup IsOpen="True" Placement="Relative" PlacementTarget="{Binding ElementName=_panel}">
    ///         <Popup.HorizontalOffset>
    ///             <MultiBinding Converter="{ExpressionConverter {}{0} / 2 - {1} / 2}">
    ///                 <Binding Path="ActualWidth" ElementName="_panel"/>
    ///                 <Binding Path="ActualWidth" ElementName="_border"/>
    ///             </MultiBinding>
    ///         </Popup.HorizontalOffset>
    ///         <Popup.VerticalOffset>
    ///             <MultiBinding Converter="{ExpressionConverter {}{0} / 2 - {1} / 2}">
    ///                 <Binding Path="ActualHeight" ElementName="_panel"/>
    ///                 <Binding Path="ActualHeight" ElementName="_border"/>
    ///             </MultiBinding>
    ///         </Popup.VerticalOffset>
    /// 
    ///         <Border x:Name="_border" Background="White" BorderThickness="1">
    ///             <Label>Here is the popup.</Label>
    ///         </Border>
    ///       </Popup>
    ///    </StackPanel>
    /// </example>
    /// <remarks>
    /// <para>
    /// The expression can be any valid C# expression as long as it uses only the operators discussed below. Bound values can be accessed
    /// within the expression by using curly brackets. For example, an expression of <c>{0}</c> will simply yield the first bound value
    /// unchanged, whereas an expression of <c>{0} * 2</c> will return the value doubled.
    /// </para>
    /// <para>
    /// The operators supported are listed below along with example usage. They are listed in decreasing order of precedence.
    /// <list type="table">
    ///     <listheader>
    ///         <term>Operator Category</term>
    ///         <term>Operator</term>
    ///         <term>Example</term>
    ///     </listheader>
    ///     <item>
    ///         <term>Unary</term>
    ///         <term>+</term>
    ///         <term>+4</term>
    ///     </item>
    ///     <item>
    ///         <term></term>
    ///         <term>-</term>
    ///         <term>-4</term>
    ///     </item>
    ///     <item>
    ///         <term></term>
    ///         <term>!</term>
    ///         <term>!true</term>
    ///     </item>
    ///     <item>
    ///         <term></term>
    ///         <term>~</term>
    ///         <term>~21</term>
    ///     </item>
    ///     <item>
    ///         <term></term>
    ///         <term>(T)</term>
    ///         <term>(long) 3</term>
    ///     </item>
    ///     <item>
    ///         <term></term>
    ///         <term>true</term>
    ///         <term>{0} == true</term>
    ///     </item>
    ///     <item>
    ///         <term></term>
    ///         <term>false</term>
    ///         <term>{0} == false</term>
    ///     </item>
    ///     <item>
    ///         <term>Multiplicative</term>
    ///         <term>*</term>
    ///         <term>4 * {0}</term>
    ///     </item>
    ///     <item>
    ///         <term></term>
    ///         <term>/</term>
    ///         <term>{0} / 4</term>
    ///     </item>
    ///     <item>
    ///         <term></term>
    ///         <term>%</term>
    ///         <term>{0} % 4</term>
    ///     </item>
    ///     <item>
    ///         <term>Additive</term>
    ///         <term>+</term>
    ///         <term>{0} + 4</term>
    ///     </item>
    ///     <item>
    ///         <term></term>
    ///         <term>-</term>
    ///         <term>{0} - 4</term>
    ///     </item>
    ///     <item>
    ///         <term>Shift</term>
    ///         <term>&lt;&lt;</term>
    ///         <term>{0} &lt;&lt; 4</term>
    ///     </item>
    ///     <item>
    ///         <term></term>
    ///         <term>&gt;&gt;</term>
    ///         <term>{0} &gt;&gt; 4</term>
    ///     </item>
    ///     <item>
    ///         <term>Relational</term>
    ///         <term>&lt;</term>
    ///         <term>{0} &lt; 50</term>
    ///     </item>
    ///     <item>
    ///         <term></term>
    ///         <term>&gt;</term>
    ///         <term>{0} &gt; 50</term>
    ///     </item>
    ///     <item>
    ///         <term></term>
    ///         <term>&lt;=</term>
    ///         <term>{0} &lt;= 50</term>
    ///     </item>
    ///     <item>
    ///         <term></term>
    ///         <term>&gt;=</term>
    ///         <term>{0} &gt;= 50</term>
    ///     </item>
    ///     <item>
    ///         <term>Equality</term>
    ///         <term>==</term>
    ///         <term>{0} == 50</term>
    ///     </item>
    ///     <item>
    ///         <term></term>
    ///         <term>!=</term>
    ///         <term>{0} != 50</term>
    ///     </item>
    ///     <item>
    ///         <term>Logical AND</term>
    ///         <term>&amp;</term>
    ///         <term>{0} &amp; 16</term>
    ///     </item>
    ///     <item>
    ///         <term>Logical OR</term>
    ///         <term>|</term>
    ///         <term>{0} | 16</term>
    ///     </item>
    ///     <item>
    ///         <term>Logical XOR</term>
    ///         <term>^</term>
    ///         <term>{0} ^ 16</term>
    ///     </item>
    ///     <item>
    ///         <term>Conditional AND</term>
    ///         <term>&amp;&amp;</term>
    ///         <term>{0} &amp;&amp; {1}</term>
    ///     </item>
    ///     <item>
    ///         <term>Conditional OR</term>
    ///         <term>||</term>
    ///         <term>{0} || {1}</term>
    ///     </item>
    ///     <item>
    ///         <term>Ternary Conditional</term>
    ///         <term>?:</term>
    ///         <term>{0} ? "yes" : "no"</term>
    ///     </item>
    ///     <item>
    ///         <term>Null Coalescing</term>
    ///         <term>??</term>
    ///         <term>{0} ?? {1} ?? "default"</term>
    ///     </item>
    /// </list>
    /// </para>
    /// </remarks>
    [MarkupExtensionReturnType(typeof(ExpressionConverter))]
    public sealed class ExpressionConverterExtension : MarkupExtension
    {
        #region Members
        private static readonly ExceptionHelper exceptionHelper = new ExceptionHelper(typeof(ExpressionConverterExtension));
        private string expression;
        #endregion

        #region Construction
        /// <summary>
        /// Initializes a new instance of the ExpressionConverterExtension class.
        /// </summary>
        public ExpressionConverterExtension()
        {
        }

        /// <summary>
        /// Initializes a new instance of the ExpressionConverterExtension class with the given expression.
        /// </summary>
        /// <param name="expression">
        /// The expression.
        /// </param>
        public ExpressionConverterExtension(string expression)
        {
            this.expression = expression;
        }
        #endregion

        #region Properties
        /// <summary>
        /// Gets or sets the expression to use in the <see cref="ExpressionConverter"/>.
        /// </summary>
        [ConstructorArgument("expression")]
        public string Expression
        {
            get { return this.expression; }
            set { this.expression = value; }
        }
        #endregion

        #region Base Class Overrides 基类方法重写
        /// <summary>
        /// Provides an instance of <see cref="ExpressionConverter"/> based on <see cref="Expression"/>.
        /// </summary>
        /// <param name="serviceProvider">
        /// An object that can provide services.
        /// </param>
        /// <returns>
        /// The instance of <see cref="ExpressionConverter"/>.
        /// </returns>
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            exceptionHelper.ResolveAndThrowIf(this.expression == null, "NoExpression");
            return new ExpressionConverter(this.expression);
        }
        #endregion
    }
    #endregion
}
